﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Net;
using HtmlAgilityPack;
using System.Web;
using System.IO;

namespace JbolanoPocketSync
{
    class Articles
    {
        string _code;
        string _token;
        string _localFolder;

        string _list;

        public Articles(string code, string token, string localFolder)
        { 
            //Constructor.
            _token = token;
            _code = code;
            _localFolder = localFolder + @"\";
        }

        public string status
        {
            get;
            set;
        }

        public string complete
        {
            get;
            set;
        }

        public object list
        {
            get { return JsonConvert.DeserializeObject<Article>(_list); }
            set { _list = value.ToString(); }
        }
        
        public string since
        {
            get;
            set;
        }

        public List<Article> listToDownload
        {
            get {return _download;}
        }

        public List<Article> listToArchive
        {
            get { return _archive; }
        }

        public List<Article> listOnRemote
        {
            get { return _articles; }
        }

        private List<Article> _articles;
        private List<Article> _download;
        private List<Article> _archive;

        public void PrepareSync(DirectoryInfo dir)
        {
            List<string> directories = new List<string>();
            List<string> files = new List<string>();

            _download = new List<Article>();
            _archive = new List<Article>();

            foreach (DirectoryInfo d in dir.GetDirectories())
            {
                directories.Add(d.Name);
            }

            foreach (FileInfo d in dir.GetFiles("*.html"))
            {
                files.Add(d.Name);
            }

            foreach (Article art in _articles)
            {
                if (directories.Contains(art.item_id))
                {
                    if (!files.Contains(RemoveBadChars(art.given_title)+".html"))
                    {
                        //Have the folder but not the HTML. So assume it has been locally readed. Archive in remote.
                        _archive.Add(art);
                    }
                } else { 
                    //To download.
                    _download.Add(art);
                }
            }
        }
        
        public void GetRemoteList(){
            oAuth oa = new oAuth(_code,_token);

            oa.detailType = "complete";
            oa.state = "unread";

            Articles arts = oa.Operation("https://getpocket.com/v3/get", oa);
            
            //??
            Dictionary<string, object> values = JsonConvert.DeserializeObject<Dictionary<string, object>>(arts._list);
            _articles = new List<Article>();

            foreach (object a in values.Values){
                Article art = JsonConvert.DeserializeObject<Article>(a.ToString());
                _articles.Add(art);   
            }
            Console.WriteLine("{0} unreaded items found in GetPocket.", _articles.Count);
        }

        public void UpdatePocket()
        {
            Console.WriteLine("Marking {0} items as readed in Pocket...", listToArchive.Count);
            oAuth oa = new oAuth(_code, _token);
            foreach(Article a in listToArchive){
                Action act = new Action("archive", a.item_id);
                oa.actions.Add(act);
            }

            oAuth arts = oa.Archive("https://getpocket.com/v3/send", oa);

            if (arts.status == "0")
            {
                Console.WriteLine("Something went wrongr while marking items as readed.");
            }
            Console.WriteLine("...Marking finished.");
        }

        public void DownloadNewContent()
        {
            Console.WriteLine("Processing {0} web pages...", listToDownload.Count);
            foreach (Article art in listToDownload)
            {
                
                WebClient x = new WebClient();
                try
                {
                    Console.WriteLine("Processing: {0}", art.given_title);
                    Uri url = new Uri(art.resolved_url);

                    HtmlAgilityPack.HtmlWeb hw = new HtmlAgilityPack.HtmlWeb();

                    System.Diagnostics.Debug.WriteLine(art.resolved_url);
                    HtmlDocument doc = hw.Load(art.resolved_url);

                    System.Diagnostics.Debug.WriteLine(art.given_title);
                    System.Diagnostics.Debug.WriteLine("-------------------------------------------------------");
                    art.given_title = RemoveBadChars(art.given_title);
                    System.Diagnostics.Debug.WriteLine(art.given_title);

                    System.IO.Directory.CreateDirectory(_localFolder + art.item_id);

                    int counter = 0;

                    ProcessLink(ref doc, ref counter, url, "//link[@href]", "href", _localFolder + art.item_id);
                    ProcessLink(ref doc, ref counter, url, "//img[@src]", "src", _localFolder + art.item_id);
                    ProcessLink(ref doc, ref counter, url, "//script[@src]", "src", _localFolder + art.item_id);
                    try
                    {
                        doc.Save(_localFolder + art.given_title + ".html");
                    }
                    catch (Exception ex)
                    {

                        System.IO.Directory.Delete(_localFolder + art.item_id);
                        System.Diagnostics.Debug.WriteLine("Error at downloading page: " + art.given_title);
                        System.Diagnostics.Debug.Write(ex.Message);
                        Console.WriteLine("Error at saving file: {0}", art.given_title);
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("ERROR!!..." + art.given_title);
                    System.Diagnostics.Debug.WriteLine(art.given_title);
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                    Console.WriteLine("Error downloading file: {0}", art.given_title);
                }

                System.Diagnostics.Debug.WriteLine("");
                
            }
        }

        public string RemoveBadChars(string fileName)
        {

            var invalidChars = System.IO.Path.GetInvalidFileNameChars();
            var cleanFileName = new string(fileName.Where(m => !invalidChars.Contains(m)).ToArray<char>());
            return cleanFileName;
        }

        private void ProcessLink(ref HtmlDocument doc, ref int counter, Uri url, string xPathElement, string atributoURL, string outputFolder)
        {
            DirectoryInfo folder = new DirectoryInfo(outputFolder);
            FileInfo file;

                foreach (HtmlNode link in doc.DocumentNode.SelectNodes(xPathElement))
                {
                    if (link.Attributes.Contains("type") && IsValidType(link.Attributes["type"].Value) || atributoURL == "src")
                    {

                        HtmlAttribute att = link.Attributes[atributoURL];

                        Uri externalURL;
                        if (att.Value.IndexOf("http") == 0)
                        {
                            externalURL = new Uri(att.Value);
                        }
                        else
                        {
                            if (att.Value.IndexOf("//") == 0)
                            {
                                externalURL = new Uri("http:" + att.Value);
                            }
                            else
                            {
                                if(att.Value.IndexOf("/") == 0){
                                    externalURL = new Uri(url.Scheme + @"://" + url.Host + att.Value);
                                }else{
                                    externalURL = new Uri(url.AbsoluteUri.Remove(url.AbsoluteUri.LastIndexOf("/"), url.AbsoluteUri.Length - url.AbsoluteUri.LastIndexOf("/")) + "/" + att.Value);
                                }                                
                            }
                        }

                        WebClient y = new WebClient();

                        string lastSegment = "";

                        char[] delimiteChar = { '.' };
                        string[] segmentos = externalURL.ToString().Split(delimiteChar);

                        foreach (string seg in segmentos)
                        {
                            lastSegment = seg;
                        }

                        char[] ch = { '&', '#', '?' };
                        foreach (char c in ch)
                        {
                            if (lastSegment.Contains(c))
                            {
                                lastSegment = lastSegment.ToString().Split(c)[0];
                            }
                        }

                        try
                        {
                            counter += 1;
                           
                            y.DownloadFile(externalURL, folder.FullName + @"\" + counter + "." + lastSegment);
                            file = new FileInfo(folder.FullName + @"\" + counter + "." + lastSegment);
                        }
                        catch (Exception ex)
                        {
                            System.Diagnostics.Debug.WriteLine(ex.Message);
                        }

                        att.Value = @"./" + folder.Name + @"/" + counter + "." + lastSegment;
                    }
                }
            }       

        private bool IsValidType(string type)
        {
            bool isValid = false;
            switch(type.ToLower()){
                case "text/javascript":
                case "image/x-icon":
                case "text/css":
                    isValid = true;
                    break;
                default:
                    isValid = false;
                    break;
            }

            return isValid;
        }   
    }
}
